//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include "Jffs2FileSystem.h"
#define _NO_CJFFS2FILESYSTEM_CLASSOBJECT_
#include "_CJffs2FileSystem.cpp"

#include "Jffs2Directory.h"
#include "Jffs2File.h"
#include <stdio.h>
#include <stdlib.h>
#include "nodelist.h"
#include <jffs2_misc.h>
#include "mutex.h"
#include "jffs2_limit.h"
#include "jffs2_macro.h"

CJffs2FileSystem::CJffs2FileSystem()
{
    m_pFileSystem = NULL;
    m_pStub = NULL;
}

CJffs2FileSystem::~CJffs2FileSystem()
{
    UnbindDriver();
    kprintf("delete jffs2 instance.\n");
}

ECode CJffs2FileSystem::GetFileSystemStat(
                /* [out] */ MemoryBuf * pFsstat)
{
#define STAT_BUF_SIZE 256
#define STRING_LEN 128
    char tmp[64], *buf = NULL;
    int len;
    if (pFsstat == NULL) {
        return E_INVALID_ARGUMENT;
    }
    wchar_t *wbuf = (wchar_t *)pFsstat->GetPayload();

    if (!pFsstat->GetCapacity()) {
        return ErrorToECODE(EINVAL, "CJffs2FileSystem::GetFileSystemStat");
    }

    if (STAT_BUF_SIZE > (UInt32)pFsstat->GetCapacity()) {
        return ErrorToECODE(EINVAL, "CJffs2FileSystem::GetFileSystemStat");
    }

    buf = (char *)malloc(sizeof(char) * STRING_LEN);
    if (NULL == buf) {
        return ErrorToECODE(ENOMEM, "CJffs2FileSystem::GetFileSystemStat");
    }

    sprintf(buf, "File System Name: JFFS2\n");
    sprintf(tmp, "Cluster Size: %u\n", JFFS2_SB_INFO(m_pFileSystem->jffs2_get_superBlock())->sector_size);
    strcat(buf, tmp);
    sprintf(tmp, "Flash has totally Size %u\n", JFFS2_SB_INFO(m_pFileSystem->jffs2_get_superBlock())->flash_size);
    strcat(buf, tmp);

    len = strlen(buf)+1;
    _strtowcs(wbuf, buf);

    pFsstat->SetUsed(2*len);

    free(buf);
    return NOERROR;
}

ECode CJffs2FileSystem::Sync()
{
    return NOERROR;
}

ECode CJffs2FileSystem::GetRoot(
                /* [out] */ IBasicDirectory * * piRoot)
{
    int error;
    if (!m_pFileSystem || !m_pRootIBasicDirector) {
        error = ENOROOT;
        goto Exit;
    }

    if (NULL == piRoot) {
        error = EINVAL;
        goto Exit;
    }

    m_pRootIBasicDirector->AddRef();
    *piRoot = m_pRootIBasicDirector;

    return NOERROR;
Exit:
    return ErrorToECODE(error, "CJffs2FileSystem::GetRoot");
}

ECode CJffs2FileSystem::Lookup(
                /* [in] */ WString wstrPath,
                /* [out] */ IBasicFile * * piBasicFile)
{
    CJffs2Directory *pBasicDir = NULL;
    CJffs2File *pBasicFile = NULL;
    n_file pFile;
    char szPathName[PATH_MAX_LEN];
    int error;

    if ((NULL == wstrPath) || (L'\0' == *wstrPath) || (wcslen(wstrPath) >= PATH_MAX_LEN) ||
        (NULL == piBasicFile)) {
        error = EINVAL;
        goto Exit;
    }

    WcsToMbs(szPathName, (const wchar_t *)wstrPath, PATH_MAX_LEN);

    error = IoOperator::jffs2_open(m_pFileSystem->jffs2_get_root(), szPathName, O_RDWR, &pFile);
    if (error != ENOERR) {
        if (error == EISDIR) {
            //We want to open a directory
            error = IoOperator::jffs2_opendir(m_pFileSystem->jffs2_get_root(), szPathName, &pFile);
            if (error != ENOERR) {
                goto Exit;
            }
        }
        else
            goto Exit;
    }

    if (pFile.f_type == ELA_FILE_TYPE_FILE) {
        pBasicFile = new CJffs2File(pFile);
        if (!pBasicFile) {
            jffs2_iput(pFile.f_data);
            error = ENOMEM;
            goto Exit;
        }
        pBasicFile->AddRef();
        *piBasicFile = pBasicFile;
    }
    else {
        assert(pFile.f_type == ELA_FILE_TYPE_DIRECTORY);
        pBasicDir = new CJffs2Directory(pFile);
        if (!pBasicDir) {
            jffs2_iput(pFile.f_data);
            error = ENOMEM;
            goto Exit;
        }
        pBasicDir->AddRef();
        *piBasicFile = pBasicDir;
    }

    return NOERROR;
Exit:
    return ErrorToECODE(error, "CJffs2FileSystem::Lookup");
}

ECode CJffs2FileSystem::BindDriver(
                /* [in] */ WString wstrDevName)
{
    int error;
    n_file file;
    struct _inode *node;
    kprintf("In BindDriver.\n");
    m_pFileSystem = new FileSystem;
    if (m_pFileSystem == NULL) {
        DPRINTF(("ERROR:New File System Object Error\n"));
        error = ENOMEM;
        goto BD_ERR_EXIT;
    }
    kprintf("Mount Jffs2, Please waiting...");

    error = m_pFileSystem->jffs2_mount((wchar_t *)wstrDevName);
    kprintf("OK.\n");

    if (error != ENOERR) {
        kprintf("Failed.\n");
        goto BD_ERR_EXIT;
    }
    /* initialize the root file struct */
    node = m_pFileSystem->jffs2_get_root();
    file.f_flag = node->i_mode & S_IRWXU;
    file.f_type = ELA_FILE_TYPE_DIRECTORY;
    file.f_offset = 0;
    file.f_data = node;
    //file.f_fileSystem = m_pFileSystem;

    m_pRootIBasicDirector = new CJffs2Directory(file);
    if (NULL == m_pRootIBasicDirector) {
        DPRINTF(("ERROR: m_pRootIBasicDirector is NULL!\n"));
        error = ENOMEM;
        goto BD_ERR_EXIT;
    }
    m_pRootIBasicDirector->AddRef();

    return NOERROR;

BD_ERR_EXIT:
    kprintf("Bind Failed.error = %d.\n", error);

    if (m_pFileSystem) {
        delete m_pFileSystem;
        m_pFileSystem = NULL;
    }
    return ErrorToECODE(error, "CJffs2FileSystem::BindDriver");
}

ECode CJffs2FileSystem::UnbindDriver()
{
    if (m_pRootIBasicDirector) {
        m_pRootIBasicDirector->Release();
        m_pRootIBasicDirector = NULL;
    }

    if (m_pFileSystem) {
        m_pFileSystem->jffs2_umount();
        delete m_pFileSystem;
        m_pFileSystem = NULL;
    }

    return NOERROR;
}

void CJffs2FileSystem::SetStub(IStub *pStub)
{
    m_pStub = pStub;
}

IStub *CJffs2FileSystem::GetStub()
{
    return m_pStub;
}

ECode CJffs2FileSystem::CalcFreeSpace(
                /* [out] */ UInt64 * pTotalNumberOfBytes,
                /* [out] */ UInt64 * pTotalNumberOfFreeBytes)
{
    struct jffs2_sb_info *c;
    if ((NULL == pTotalNumberOfBytes) && (NULL == pTotalNumberOfFreeBytes)) {
        return ErrorToECODE(EINVAL, "CJffs2FileSystem::CalcFreeSpace");
    }

    if (NULL == m_pFileSystem) {
        return ErrorToECODE(ENOROOT, "CJffs2FileSystem::CalcFreeSpace");
    }

    c = JFFS2_SB_INFO(m_pFileSystem->jffs2_get_superBlock());

    if (pTotalNumberOfBytes != NULL) {
        *pTotalNumberOfBytes = (UInt64)c->flash_size;
    }

    if (pTotalNumberOfFreeBytes != NULL) {
        *pTotalNumberOfFreeBytes = (UInt64)c->free_size;
    }

    return NOERROR;
}
